/* Copyright 2017, 2018, Gabriel Czernikier
 *
 * This file is part of Notehaus.
 * Notehaus is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * Notehaus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with Notehaus.  If not, see <http://www.gnu.org/licenses/>.
 */
    // Example of proto_id_item with 2 parameters: tok = elements of (ShortyDescriptor_tok, TypeDescriptor_tok, TypeDescriptor_tok, TypeDescriptor_tok)
    // up_bounds_ary = {header_up_bound=C, str_id_up_bound=header_up_bound, type_id_up_bound=header_up_bound, proto_id_up_bound=header_up_bound, type_list_up_bound=header_up_bound, str_dt_up_bound=header_up_bound};
    // base_address_equiv_up_bounds_idx=proto_id_up_bound_idx
    // alignment_ary = {header_alignment=1, str_id_alignment=4, type_id_alignment=4, proto_id_alignment=4, type_list_alignment=4, str_dt_alignment=1}
    // PROCESS P1: from initial tok = ShortyDescriptor_tok thru last of every add_str_dt(tok) is executed, all making up str_id[1..n].
    // Let str_id = add_str_dt(tok);
    /* step s1: str_dt={unpacked_data, unpacked_size}, str_id={str_dt}, proto_id.shorty_idx=str_id
     * step s2: from ordered without duplicates str_id[1..n] as ostr_id[1..n] by (comparisson function, str_id[1..n].str_dt.{unpacked_data, unpacked_size}) ==> { str_id[i=1..n from ostr_id[1..n]].idx=i, str_id[j=1..n where in undisduplicated ostr_id[1..n] ostr_id[j] was a duplicate of the disduplicated ostr_id[i]].idx=i, str_id[1..n from ostr_id[1..n]].str_dt={packed_data, packed_size} }
     * step s3: up_bounds_ary[str_id_up_bound_idx..n]+=K*n in ostr_id[1..n]
     * step s4: ostr_id[i=1..n].relative_offset=str_id[i-1 from ostr_id[1..n]].str_dt.packed_size+ostr_id[i-1].relative_offset
     * step s5: up_bounds_ary[str_dt_up_bound_idx..n]+=str_id[n from ostr_id[1..n]].str_dt.packed_size+ostr_id[n].relative_offset
     */
    // PROCESS P2 minus P1 (i.e. P1 substracted from P2): from initial tok = TypeDescriptor_tok (1) thru last of every type_id={str_id} is executed, all making up type_id[1..n]
    // Let str_id = add_str_dt(tok);
    /* step s1: type_id={str_id}, proto_id.return_type_idx = type_id
     * step s2: from ordered without duplicates type_id[1..n] as otype_id[1..n] by (comparisson function, type_id[i=1..n].str_id.idx) ==> { type_id[i=1..n from otype_id[1..n]].idx=i, type_id[j=1..n where in undisduplicated otype_id[1..n] otype_id[j] was a duplicate of the disduplicated otype_id[i]].idx=i }
     * step s3: up_bounds_ary[type_id_up_bound_idx..n]+=K_2*n as n in otype_id[1..n]
     */
    // PROCESS P3 minus P1 minus P2 (i.e. P1 and P2 substracted from P3): from initial tok = TypeDescriptor_tok (2), (3), (4) thru last of every type_list[L]=type_id is executed, all making up type_list[1..n][1..L].
    // Let str_id/*(2)*/ = add_str_dt(tok/*(2)*/); str_id/*(3)*/ = add_str_dt(tok/*(3)*/);  str_id/*(4)*/ = add_str_dt(tok/*(4)*/);
    /* step s1: type_id(2)=str_id(2), type_id(3)=str_id(3), type_id(4)=str_id(4), type_list[1]=type_id(2), type_list[2]=type_id(3), type_list[3]=type_id(4), L=3 in type_list[1..L], proto_id.parameters_list=type_list
     * step s2: from ordered without duplicates type_list[1..n (not the L-dimmension)] as otype_list[1..n] by (comparisson function, type_list[1..n][1..L]) ==> { type_list[i=1..n from otype_list[1..n]].relative_offset=K_3+K_4*L as L in type_list[i-1][1..L]+type_list[i-1].relative_offset, type_list[j=1..n where in undisduplicated otype_list[1..n] otype_list[j] was a duplicate of the disduplicated otype_list[i]].relative_offset=type_list[i].relative_offset }
     * step s3: up_bounds_ary[type_list_up_bound_idx..n]+=K_3+K_4*L as L in type_list[1..n][1..L]
     */
    // PROCESS P4 minus P1 minus P2 minus P3 (i.e. P1, P2 and P3 substracted from P4): from initial proto_id thru last of every proto_id complete, all making up proto_id[1..n]
    /* step s3: from ordered without duplicates proto_id[1..n] as oproto_id[1..n] by (comparisson function, proto_id[i=1..n].{return_type_idx,parameters_list}) ==> { proto_id[i=1..n from oproto_id[1..n]].idx=i, proto_id[j=1..n where in undisduplicated oproto_id[1..n] oproto_id[j] was a duplicate of the disduplicated oproto_id[i]].idx=i }
     * step s1: up_bound_ary[proto_id_up_bound_idx..n] += K_5*n as n in oproto_id[1..n]
     */
    // PROCESS P5 (modeled after assumption type_list[1..n] lower and str_dt[1..n] higher addressed)
    // General formula for next padding to write: next_padding_to_write=(next_item_alignment-last_write_up_bound_address%next_item_alignment)%next_item_alignment
    
    // step s1: pack_write(str_id[1..n from ostr_id[1..n]], /*for address referencing from str_id to str_dt (*/ ostr_id[1..n], up_bounds_ary[str_dt_idx-1],/*) for align (*/up_bounds_ary[str_id_idx-1], alignment_ary[str_id_idx] /*)*/ )
    // step s2: pack_write(type_id[1..n from otype_id[1..n]], str_id[1..n], up_bounds_ary[type_id_idx-1], alignment_ary[type_id_idx])
    // step s3: pack_write(proto_id[1..n from oproto_id[1..n]], /*for address referencing from proto_id to type_list (*/ type_list[1..n], up_bounds_ary[type_list_idx-1],/*) for align (*/ up_bounds_ary[proto_id_idx-1], alignment_ary[proto_id_idx] /*)*/)
    // step s4: pack_write(type_list[1..n] from otype_list[1..n], up_bounds_ary[type_list_idx-1], alignment_ary[type_list_idx])
    // step s5: write_packed(str_dt[0..n], up_bounds_ary[str_dt_idx-1], alignment_ary[str_dt_idx])
#ifndef __NOTEHAUS_COMPOSER
#define __NOTEHAUS_COMPOSER

#include <stdlib.h>
#include "lgwseq.h"
#include "types.h"
// Exported variables
extern int up_bounds_ary[];
extern int alignment_ary[];
extern int majors_size_ary[];
extern unsigned char *buffer[];
extern size_t buf_len[];
extern size_t buf_offset[];
// Constants to track and frame computed bounds
#define NH_UNUSED_IDX 0
#define NH_HEADER_IDX 1
#define NH_STR_ID_IDX 2
#define NH_TYPE_ID_IDX 3
#define NH_PROTO_ID_IDX 4
#define NH_TYPE_LIST_IDX 5
#define NH_MAP_LIST_IDX 6
#define NH_STR_DT_IDX 7
#define NH_CLS_DT_IDX 8
#define NH_UP_BOUNDS_SZ 9
#define NH_DATA_LBOUND (NH_TYPE_LIST_IDX)
#define NH_DATA_UBOUND (NH_UP_BOUNDS_SZ)
// Constants that set known-on-beforehand values for the sake of computing bounds (aside the values themselves could be in turn an item data to other items).
#define NH_HEADER_UP_BOUND 0x70
struct str_dt_st {
  lgwseq_t *unpacked_data;
  void *packed_data;
};
struct str_id_st {
  struct str_dt_st *str_dt;
  uint32_t idx;
  struct list_head str_id_list;
};
struct ostr_id_st {
  struct str_id_st *str_id;
  char bool_duplicate;
  struct ostr_id_st *duplicate_major;   // TODO probably useless.
  struct ostr_id_st *next_major;
  uint32_t relative_offset;
} *ostr_id_ary;  // TODO if the struct's are going to stay here in the header, then the pointers should be local to the compilation unit where they are used.
struct type_id_st {
  struct str_id_st *str_id;
  uint32_t idx;  // TODO this item is to be changed to support common arithmetic, fullname_compar function is at least one operating in such common arithmetic. There's more to be said about this item even if the former is ignored. Care: Some cases will need for idx to be uint32_t as others will do so for an uint16_t. Examples of each one are proto_id_item.return_type_idx (proto_id_st.return_type_idx.idx) that encodes a uint and type_item.type_idx (type_list_st.type_member_head.type_id.idx), a ushort. Appropriate type conversion may be needed. Honoring the storage requirements' general case, its declared type is 32-bit wide.
  struct list_head type_id_list;
};
struct otype_id_st {
  struct type_id_st *type_id;
  char bool_duplicate;
  struct otype_id_st *duplicate_major;   // TODO probably useless.
  struct otype_id_st *next_major;
} *otype_id_ary;
struct type_member_st {
  struct type_id_st *type_id;
  struct list_head type_member_list;
};
struct type_list_st {
  uint32_t nb_members;   // number of members
  uint32_t relative_offset;
  struct list_head type_list_list;   // List of type_list's (with which relation is "aggregated in")
  struct list_head type_member_head;   // This list's members (with which relation is "aggregates")
};
struct otype_list_st {
  struct type_list_st *type_list;
  char bool_duplicate;
  struct otype_list_st *duplicate_major;   // TODO probably useless.
  struct otype_list_st *next_major;
} *otype_list_ary;
struct proto_id_st {
  struct str_id_st *shorty_idx;
  struct type_id_st *return_type_idx;
  struct type_list_st *parameters_list;
  uint16_t idx;
  struct list_head proto_id_list;
};
struct oproto_id_st {
  struct proto_id_st *proto_id;
  char bool_duplicate;
  struct oproto_id_st *duplicate_major;   // TODO probably useless.
  struct oproto_id_st *next_major;
} *oproto_id_ary;
 
struct str_id_st *add_str_id(lgwseq_t *for_str_dt);
struct type_id_st *add_type_id(struct str_id_st *str_idx);
void add_type_member(struct type_id_st *type_id);
struct type_list_st *add_type_list();
struct proto_id_st *add_proto_id(struct type_id_st *return_type_idx, struct type_list_st *parameters_list);
int ostr_id_st_compar(struct ostr_id_st *ostr_id_1, struct ostr_id_st *ostr_id_2);
int otype_id_st_compar(struct otype_id_st *otype_id_1, struct otype_id_st *otype_id_2);
int otype_list_st_compar(struct otype_list_st *otype_list_1, struct otype_list_st *otype_list_2);
void bounds_move(int idx, unsigned int amount);
void build_ostr_id_and_pack_str_dt();
void build_otype_id();
void build_otype_list();
void align_bounds();
void build_oproto_id();
void align_bounds();
void pack_ostr_id_ary();
void pack_otype_id();
void pack_otype_list();
void pack_oproto_id();
/* class_def. Synopsis/API usage outline:
 * A Class or Interface declaration synthesyses a qualified RefType, as is the case for stanzas "package bar" and "public class Foo". Then, it would be handled as follows:
 * type_id = add_type_id(qualified RefType) // add_type_id defined in "type.c"
 * class_def = class_def_new(type_id...)
 * A Class or Interface declaration occurs in a place in which a nested type is constitued, esquematicaly resumed in "class A { class B {} }". A typical handling for that case is as follows:
 * stack_push(&class_def->nesting_stack, &class_nesting_stack_head) // stack_push function defined in types.h
 * type_id = add_type_id(qualified RefType)
 * class_def = class_def_new(type_id...)
 * When the nesting context closes, a handling like the following is suitable:
 * stack_pop(class_def, class_nesting_stack_head, struct class_def_st, nesting_stack) // stack_pop defined in types.h
 */
struct class_def_st {
	struct type_id_st *clstyp;   // class type
	struct type_id_st *parenttyp;   // parent class type
	struct type_list_st *parent_iface;   // parent interfaces, elements of type struct type_id_st*
	struct class_data_st *class_data;
	struct list_head clsdef_lst;   // the list this item is a member of
	struct list_head nesting_stack; // class_defs stack on here as their lexical context enclose one another, e.g. this text "class A { class B {} }" turns into B on top of A onto nesting_stack.

}

struct class_def_st *add_class_def(struct type_id_st *clstyp);
struct field_id_st {
	struct type_id_st *class_idx;
	struct type_id_st *type_idx;
	struct str_id_st *name_idx;
}
struct method_id_st {
	struct type_id_st *class_idx;
	struct proto_id_st *proto_idx;
	struct str_id_st *name_idx;
}

struct method_id_st *add_method_id(struct type_id_st *class_idx, struct proto_id_st *proto_idx, struct str_id_st *name_idx);

#define BUFFER_WRITE(BUFFER,BUFLEN,DATA,OFFSET,LEN) while((OFFSET)+(LEN)>(BUFLEN)) \
  (BUFLEN) = (BUFLEN)==0?512:(BUFLEN)*2; \
  if(((BUFFER) = realloc((BUFFER), (BUFLEN)))==NULL) { \
    fprintf(stderr, "%s: %s.", __func__, strerror(ENOMEM)); \
    exit(EXIT_FAILURE); \
  } \
  memcpy((BUFFER)+(OFFSET), (DATA), (LEN)); \
  (OFFSET) += (LEN);

#endif
